package com.example.demozy.controller;

import com.example.demozy.api.CommonResult;
import com.example.demozy.entity.Challenge;
import com.example.demozy.entity.HistoryModel;
import com.example.demozy.entity.Participant;
import com.example.demozy.entity.SheetCondition;
import com.example.demozy.service.ChallengeService;
import com.example.demozy.service.ConditionSheetService;
import com.example.demozy.service.StockService;
import com.example.demozy.service.UserService;
import com.example.demozy.vo.BackTestResultVO;
import com.example.demozy.vo.RealTimeRankingVO;
import com.example.demozy.vo.SingleBackTestResultVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.sql.Timestamp;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * @author 王成龙
 */
@RestController
public class ChallengeController {
    // **************** 公开变量

    // **************** 私有变量

    @Autowired
    private ChallengeService challengeService;

    @Autowired
    private UserService userService;

    @Autowired
    private ConditionSheetService conditionSheetService;

    @Autowired
    private StockService stockService;

    // **************** 继承方法

    // **************** 公开方法

    /**
     * 发起挑战赛
     * @param challenge
     * @return
     */
    @PostMapping(value = "/launchChallenge")
    public CommonResult launchChallenge(@RequestBody Challenge challenge) {
        String challengeName = challenge.getChallengeName();
        String introduction = challenge.getIntroduction();
        Timestamp startTime = challenge.getStartTime();
        Timestamp endTime = challenge.getEndTime();
        String stockCode = challenge.getStockCode();
        double startingFunds = challenge.getStartingFunds();
        int maxParticipantsNumber = challenge.getMaxParticipantsNumber();
        //插入操作
        challengeService
                .saveChallenge(challengeName, introduction, startTime, endTime,
                               stockCode, startingFunds, maxParticipantsNumber);
        //获取刚刚插入的挑战赛id
        int challengeId = challengeService.getMaxChallengeId();
        //返回成功结果
        return CommonResult
                .success(challengeService.getChallengeById(challengeId),
                         "发起挑战赛成功");
    }

    /**
     * 获取正在进行的挑战赛列表
     *
     * @return
     */
    @PostMapping(value = "/challengeInProgress")
    public CommonResult showChallengeInProgress() {
        //得到正在进行的挑战赛
        List<Challenge> challengeList =
                challengeService.listChallengeInProgress();
        //返回正在进行的挑战赛列表和成功信息
        return CommonResult.success(challengeList, "获取正在进行的挑战赛列表成功");
    }

    /**
     * 获取已结束的挑战赛列表
     *
     * @return
     */
    @PostMapping(value = "/challengeEnded")
    public CommonResult showChallengeEnded() {
        //得到已结束的挑战赛列表
        List<Challenge> challengeList = challengeService.listChallengeEnded();
        //返回已结束的挑战赛列表和成功信息
        return CommonResult.success(challengeList, "获取已结束的挑战赛列表成功");
    }

    /**
     * 报名参加挑战赛
     *
     * @param challengeId
     * @param userId
     * @param conditionSheetId
     * @return
     */
    @PostMapping(value = "/signForChallenge")
    public CommonResult singForChallenge(
            @RequestParam("challengeId") int challengeId,
            @RequestParam("userId") int userId,
            @RequestParam("conditionSheetId") String conditionSheetId) {
        //判断用户是否已经参加该挑战赛
        if (challengeService.isRepeated(userId, challengeId)) {
            return CommonResult.failed("您已报名参加该挑战赛，无需再次报名");
        }
        //分别获取对应id的挑战赛的当前参赛人数与最大参赛人数
        int curNumber = challengeService.getParticipantsNumberById(challengeId);
        int maxNumber =
                challengeService.getMaxParticipantsNumberById(challengeId);
        if (curNumber >= maxNumber) {
            return CommonResult.failed("当前挑战赛报名人数已满");
        }
        //获取该挑战赛并返回
        Challenge challengeInfo =
                challengeService.getChallengeById(challengeId);
        double startingFunds = challengeInfo.getStartingFunds();
        //调用service,返回正确结果
        challengeService
                .signForChallenge(challengeId, userId, conditionSheetId, startingFunds);
        return CommonResult.success(null, "报名成功");
    }

    /**
     * 获取挑战赛详细信息
     *
     * @param challengeId
     * @return
     */
    @PostMapping(value = "/getChallengeInfo")
    public CommonResult getChallengeInfo(
            @RequestParam("challengeId") int challengeId) {
        //获取该挑战赛并返回
        Challenge challengeInfo =
                challengeService.getChallengeById(challengeId);
        return CommonResult.success(challengeInfo, "获取挑战赛详细信息成功");
    }

    /**
     * 对应挑战赛的实时排名
     *
     * @param challengeId
     * @return
     */
    @PostMapping(value = "/realTimeRanking")
    public CommonResult realTimeRanking(
            @RequestParam("challengeId") int challengeId) {
        List<RealTimeRankingVO> realTimeRankingVOList = new ArrayList<>();
        //对应挑战赛的参赛用户列表
        List<Participant> participantList =
                challengeService.listParticipantsById(challengeId);
        if (participantList.size() == 0) {
            return CommonResult.failed("未查询到信息");
        }
        Collections.sort(participantList, new Comparator<Participant>() {
            @Override
            public int compare(Participant p1, Participant p2) {
                return p1.getYield() == p2.getYield() ? 0 :
                        (p1.getYield() < p2.getYield() ? 1 : -1);
            }
        });
        //将信息存储到VO中返回前端
        for (Participant participant : participantList) {
            String account = userService.getUserByUserId(participant.getUserId()).getAccount();
            RealTimeRankingVO realTimeRankingVo = new RealTimeRankingVO();
            realTimeRankingVo.setAccount(account);
            realTimeRankingVo.setYield(participant.getYield());
            realTimeRankingVo.setUserId(participant.getUserId());
            realTimeRankingVOList.add(realTimeRankingVo);
        }
        return CommonResult.success(realTimeRankingVOList, "获取实时排名成功");
    }

    /**
     * 获取该用户参加的正在进行的挑战赛列表
     *
     * @param userId
     * @return
     */
    @PostMapping(value = "/challengeInProgressUser")
    public CommonResult challengesInProgressUser(
            @RequestParam("userId") int userId) {
        //获取该用户参加的正在进行的挑战赛列表，若列表长度为0，则返回错误信息
        List<Challenge> challengeList =
                challengeService.listChallengesByUserId(userId, 0);
        if (challengeList.size() == 0) {
            return CommonResult.failed("未查询到结果");
        }
        return CommonResult.success(challengeList, "获取用户参加的正在进行的挑战赛成功");
    }

    /**
     * 获取该用户参加的已结束的挑战赛
     *
     * @param userId
     * @return
     */
    @PostMapping(value = "/challengeEndedUser")
    public CommonResult challengesEndedUser(
            @RequestParam("userId") int userId) {
        //获取该用户参加的已结束的挑战赛列表，若列表长度为0，则返回错误信息
        List<Challenge> challengeList =
                challengeService.listChallengesByUserId(userId, 1);
        if (challengeList.size() == 0) {
            return CommonResult.failed("未查询到结果");
        }
        return CommonResult.success(challengeList, "获取用户参加的已结束的挑战赛成功");
    }

    /**
     * 验证用户是否已经参加对应id的挑战赛
     * @param challengeId
     * @param userId
     * @return
     */
    @PostMapping(value = "/checkUserRepeats")
    public CommonResult checkUserRepeats(
            @RequestParam("challengeId") int challengeId,
            @RequestParam("userId") int userId) {
        if (challengeService.isRepeated(userId, challengeId)) {
            return CommonResult.failed("用户已参加该挑战赛");
        } else {
            return CommonResult.success(null, "用户未参加该挑战赛");
        }
    }

    /**
     * 结束挑战赛
     * @return
     */
    @PostMapping(value = "/endChallenge")
    public CommonResult endChallenge() {
        challengeService.endChallenge();
        return CommonResult.success(null,"检查挑战赛是否超时");
    }

    @PostMapping(value = "/compete")
    public CommonResult compete(@RequestParam("challengeId") int challengeId, @RequestParam("userId") int userId) throws
            ParseException {
        String conditionSheetId = challengeService.getConditionSheetIdByIds(challengeId, userId);
        Challenge challenge = challengeService.getChallengeById(challengeId);
        String stockCode = challenge.getStockCode();
        Timestamp startTime = challenge.getStartTime();
        Timestamp endTime = challenge.getEndTime();

        DecimalFormat df4 = new DecimalFormat("#.0000");
        DecimalFormat df2 = new DecimalFormat("#.00");
        /**
         * 获取条件单参数。
         */
        ArrayList<SheetCondition> sheetList =
                (ArrayList<SheetCondition>) conditionSheetService.listConditionSheetsConditions(conditionSheetId);
        ArrayList<SheetCondition> conditions = new ArrayList();
        double buyPosition = 0;
        double sellPosition = 0;
        for (int i = 0; i < sheetList.size(); i++){
            if (!sheetList.get(i).getConditionId().equals("4")){
                conditions.add(sheetList.get(i));
            }else {
                if (sheetList.get(i).getOrderType().equals("0")){
                    buyPosition = Double.parseDouble(sheetList.get(i).getConditionParameter());
                }else if (sheetList.get(i).getOrderType().equals("1")){
                    sellPosition = Double.parseDouble(sheetList.get(i).getConditionParameter());
                }
            }
        }
        if (conditions.size() == 0){
            return CommonResult.failed("无条件单参数");
        }

        List<HistoryModel> historyModels = stockService.getStockByCode(stockCode);
        int startFlag = 0;
        int endFlag = historyModels.size() -1;
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        for (int i = 1; i < historyModels.size(); i++){
            if (format.parse(historyModels.get(i-1).day).before(startTime) &&
                    format.parse(historyModels.get(i).day).after(startTime)){
                startFlag = i;
            }
            if (format.parse(historyModels.get(i-1).day).before(endTime) &&
                    format.parse(historyModels.get(i).day).after(endTime)){
                endFlag = i;
                break;
            }
        }
        historyModels = historyModels.subList(startFlag, endFlag);

        /**
         * 默认条件单正向突破卖，逆向突破买。
         */
        int backTestType = 0;

        double property = challenge.getStartingFunds();
        double initialCapital = challenge.getStartingFunds();
        double capital = challenge.getStartingFunds();
        //股数
        int share = 0;
        //逆向突破点
        double buyPrice = 0;
        //正向突破点
        double sellPrice = 0;
        if (conditions.size() == 1){
            if (conditions.get(0).getConditionId().equals("1") && conditions.get(0).getOrderType().equals("1")){
                backTestType = 1;
                sellPrice = Double.parseDouble(conditions.get(0).getConditionParameter());
            }else if (conditions.get(0).getConditionId().equals("2") && conditions.get(0).getOrderType().equals("0")){
                backTestType = 1;
                buyPrice = Double.parseDouble(conditions.get(0).getConditionParameter());
            }else if (conditions.get(0).getConditionId().equals("1") && conditions.get(0).getOrderType().equals("0")){
                backTestType = 2;
                buyPrice = Double.parseDouble(conditions.get(0).getConditionParameter());
            }else if (conditions.get(0).getConditionId().equals("2") && conditions.get(0).getOrderType().equals("1")){
                backTestType = 2;
                sellPrice = Double.parseDouble(conditions.get(0).getConditionParameter());
            }else {
                return CommonResult.failed("条件单参数错误");
            }
        }else {
            for (int i = 0; i < conditions.size(); i++){
                if (conditions.get(i).getConditionId().equals("1") && conditions.get(i).getOrderType().equals("1")){
                    backTestType = 1;
                    sellPrice = Double.parseDouble(conditions.get(i).getConditionParameter());
                }else if (conditions.get(i).getConditionId().equals("2") && conditions.get(i).getOrderType().equals("0")){
                    backTestType = 1;
                    buyPrice = Double.parseDouble(conditions.get(i).getConditionParameter());
                }else if (conditions.get(i).getConditionId().equals("1") && conditions.get(i).getOrderType().equals("0")){
                    backTestType = 2;
                    buyPrice = Double.parseDouble(conditions.get(i).getConditionParameter());
                }else if (conditions.get(i).getConditionId().equals("2") && conditions.get(i).getOrderType().equals("1")){
                    backTestType = 2;
                    sellPrice = Double.parseDouble(conditions.get(i).getConditionParameter());
                }else {
                    return CommonResult.failed("条件单参数错误");
                }
            }
        }
        buyPosition = buyPosition / 100;
        sellPosition = sellPosition / 100;
        BackTestResultVO backTestResult = new BackTestResultVO();

        switch (backTestType){
            case 0:
                return CommonResult.failed("回测方法未定义");
            case 1:
                for (int i = 0; i < historyModels.size(); i++){
                    SingleBackTestResultVO singleBackTestResult = new SingleBackTestResultVO();
                    singleBackTestResult.setDay(format.parse(historyModels.get(i).day));
                    singleBackTestResult.setOpen(Double.parseDouble(historyModels.get(i).open));
                    singleBackTestResult.setHigh(Double.parseDouble(historyModels.get(i).high));
                    singleBackTestResult.setLow(Double.parseDouble(historyModels.get(i).low));
                    singleBackTestResult.setClose(Double.parseDouble(historyModels.get(i).close));
                    double price = Double.parseDouble(historyModels.get(i).close);
                    property = capital + share * price;
                    if (capital > 0){
                        singleBackTestResult.setTransactionPrice(price);
                        if (price >= buyPrice){
                            property = capital + share * price;
                            int buyShare = 0;
                            if ((property * buyPosition) <= capital){
                                buyShare = (int) Math.floor(property * buyPosition / price);
                                share = share + buyShare;
                            }else {
                                if (capital > price){
                                    buyShare = (int) Math.floor(capital / price);
                                    share = share + buyShare;
                                }
                            }
                            capital = capital - buyShare * price;
                            singleBackTestResult.setBuyShare(buyShare);
                        }
                    }
                    if (share > 0){
                        singleBackTestResult.setTransactionPrice(price);
                        if (price <= sellPrice){
                            property = capital + share * price;
                            int sellShare = (int) Math.floor(property * sellPosition / price);
                            if (sellShare <= share){
                                share = share - sellShare;
                                capital = capital + sellShare * price;
                            }else {
                                capital = capital + share * price;
                                share = 0;
                            }
                            singleBackTestResult.setSellShare(sellShare);
                        }
                    }
                    singleBackTestResult.setShare(share);
                    singleBackTestResult.setCapital(capital);
                    singleBackTestResult.setProperty(property);
                    backTestResult.addSingleBackTestResultVO(singleBackTestResult);
                }
                break;
            case 2:
                for (int i = 0; i < historyModels.size(); i++){
                    SingleBackTestResultVO singleBackTestResult = new SingleBackTestResultVO();
                    singleBackTestResult.setDay(format.parse(historyModels.get(i).day));
                    singleBackTestResult.setOpen(Double.parseDouble(historyModels.get(i).open));
                    singleBackTestResult.setHigh(Double.parseDouble(historyModels.get(i).high));
                    singleBackTestResult.setLow(Double.parseDouble(historyModels.get(i).low));
                    singleBackTestResult.setClose(Double.parseDouble(historyModels.get(i).close));
                    double price = Double.parseDouble(historyModels.get(i).close);
                    property = capital + share * price;
                    if (capital > 0){
                        singleBackTestResult.setTransactionPrice(price);
                        if (price <= buyPrice){
                            property = capital + share * price;
                            int buyShare = 0;
                            if ((property * buyPosition) <= capital){
                                buyShare = (int) Math.floor(property * buyPosition / price);
                                share = share + buyShare;
                            }else {
                                if (capital > price){
                                    buyShare = (int) Math.floor(capital / price);
                                    share = share + buyShare;
                                }
                            }
                            capital = capital - buyShare * price;
                            singleBackTestResult.setBuyShare(buyShare);
                        }
                    }
                    if (share > 0){
                        singleBackTestResult.setTransactionPrice(price);
                        if (price >= sellPrice){
                            property = capital + share * price;
                            int sellShare = (int) Math.floor(property * sellPosition / price);
                            if (sellShare <= share){
                                share = share - sellShare;
                                capital = capital + sellShare * price;
                            }else {
                                capital = capital + share * price;
                                share = 0;
                            }
                            singleBackTestResult.setSellShare(sellShare);
                        }
                    }
                    singleBackTestResult.setShare(share);
                    singleBackTestResult.setCapital(capital);
                    singleBackTestResult.setProperty(property);
                    backTestResult.addSingleBackTestResultVO(singleBackTestResult);
                }
                break;
        }

        double price = Double.parseDouble(historyModels.get(historyModels.size() - 1).close);
        capital = Double.parseDouble(df2.format(capital));
        property = Double.parseDouble(df2.format(capital + share * price));
        double yield = Double.parseDouble(df4.format((property - initialCapital) / initialCapital));

        backTestResult.setInitialCapital(initialCapital);
        backTestResult.setCapital(capital);
        backTestResult.setProperty(property);
        backTestResult.setShare(share);
        backTestResult.setTransactionPrice(price);
        backTestResult.setYield(yield);

        challengeService.updateYield(challengeId, userId, yield);

        return CommonResult.success(backTestResult);
    }
    // **************** 私有方法

}
